Formula 1 (1950-2022) World Championship Data Exploration¶

1.Introduction¶

1.1 About Formula 1¶

Formula One (a.k.a. F1) is an international auto racing sport. It is the highest level of single-seat, open-wheel and open-cockpit professional motor racing contest and is is governed and sanctioned by a world body called the FIA − Fédération Internationale de l'Automobile(FIA).The name ‘Formula’ comes from the set of rules that the participating cars and drivers must follow.F1 season consists of a series of races, known as Grands Prix, which take place worldwide on purpose-built circuits and on public roads.

1.2 Setup¶

Project setup¶

In [4]:
import pandas as pd
import numpy as np
import datetime as dt
import matplotlib.pyplot as plt
import seaborn as sns
import os
import warnings
import folium
import ipywidgets as widgets
import plotly.express as px
import plotly.graph_objects as go
import plotly.offline as pyo
from IPython.display import display, HTML
pyo.init_notebook_mode()

display(HTML("<style>.container { width:100% !important; }</style>"))

pd.set_option('display.max_columns', None)
pd.set_option('display.max_colwidth', None)
pd.set_option('display.max_rows', 50)
warnings.filterwarnings("ignore")

1.3 Datasets¶

About Dataset¶

The dataset consists of all information on the Formula 1 such as races, drivers, constructors, qualifying, circuits, lap times, pit stops, championships etc. from 1950 till the end of 2022 season.
Full dataset is available at http://ergast.com/mrd/.

Reading data¶

In [5]:
#Reading all data sets
data_url="https://github.com/madrian98/Formula1DataExploration/blob/main/Data"
data_raw="?raw=true"
races = pd.read_csv((data_url+"/races.csv"+data_raw))
drivers = pd.read_csv((data_url+"/drivers.csv"+data_raw))
constructors = pd.read_csv((data_url+"/constructors.csv"+data_raw))
results = pd.read_csv((data_url+"/results.csv"+data_raw))
circuits = pd.read_csv((data_url+"/circuits.csv"+data_raw))

Data information¶

In [6]:
#Races
races.info()
#Drivers
drivers.info()
#Constructors
constructors.info()
#Results
results.info()
#Circuits
circuits.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1079 entries, 0 to 1078
Data columns (total 18 columns):
 #   Column       Non-Null Count  Dtype 
---  ------       --------------  ----- 
 0   raceId       1079 non-null   int64 
 1   year         1079 non-null   int64 
 2   round        1079 non-null   int64 
 3   circuitId    1079 non-null   int64 
 4   name         1079 non-null   object
 5   date         1079 non-null   object
 6   time         1079 non-null   object
 7   url          1079 non-null   object
 8   fp1_date     1079 non-null   object
 9   fp1_time     1079 non-null   object
 10  fp2_date     1079 non-null   object
 11  fp2_time     1079 non-null   object
 12  fp3_date     1079 non-null   object
 13  fp3_time     1079 non-null   object
 14  quali_date   1079 non-null   object
 15  quali_time   1079 non-null   object
 16  sprint_date  1079 non-null   object
 17  sprint_time  1079 non-null   object
dtypes: int64(4), object(14)
memory usage: 151.9+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 855 entries, 0 to 854
Data columns (total 9 columns):
 #   Column       Non-Null Count  Dtype 
---  ------       --------------  ----- 
 0   driverId     855 non-null    int64 
 1   driverRef    855 non-null    object
 2   number       855 non-null    object
 3   code         855 non-null    object
 4   forename     855 non-null    object
 5   surname      855 non-null    object
 6   dob          855 non-null    object
 7   nationality  855 non-null    object
 8   url          855 non-null    object
dtypes: int64(1), object(8)
memory usage: 60.2+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 211 entries, 0 to 210
Data columns (total 5 columns):
 #   Column          Non-Null Count  Dtype 
---  ------          --------------  ----- 
 0   constructorId   211 non-null    int64 
 1   constructorRef  211 non-null    object
 2   name            211 non-null    object
 3   nationality     211 non-null    object
 4   url             211 non-null    object
dtypes: int64(1), object(4)
memory usage: 8.4+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25840 entries, 0 to 25839
Data columns (total 18 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   resultId         25840 non-null  int64  
 1   raceId           25840 non-null  int64  
 2   driverId         25840 non-null  int64  
 3   constructorId    25840 non-null  int64  
 4   number           25840 non-null  object 
 5   grid             25840 non-null  int64  
 6   position         25840 non-null  object 
 7   positionText     25840 non-null  object 
 8   positionOrder    25840 non-null  int64  
 9   points           25840 non-null  float64
 10  laps             25840 non-null  int64  
 11  time             25840 non-null  object 
 12  milliseconds     25840 non-null  object 
 13  fastestLap       25840 non-null  object 
 14  rank             25840 non-null  object 
 15  fastestLapTime   25840 non-null  object 
 16  fastestLapSpeed  25840 non-null  object 
 17  statusId         25840 non-null  int64  
dtypes: float64(1), int64(8), object(9)
memory usage: 3.5+ MB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 76 entries, 0 to 75
Data columns (total 9 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   circuitId   76 non-null     int64  
 1   circuitRef  76 non-null     object 
 2   name        76 non-null     object 
 3   location    76 non-null     object 
 4   country     76 non-null     object 
 5   lat         76 non-null     float64
 6   lng         76 non-null     float64
 7   alt         76 non-null     object 
 8   url         76 non-null     object 
dtypes: float64(2), int64(1), object(6)
memory usage: 5.5+ KB

1.4 Data pre-processing¶

In [7]:
#joining results on drivers on driverID(inner join)
dfDriver_res = pd.merge(results,drivers,on='driverId')
#joining previously created table on races by raceID(inner join)
dfConstructor_res = pd.merge(dfDriver_res,races,on='raceId')
#joining previously created table on constructors by constructorID(inner join)
dfRaceResults = pd.merge(dfConstructor_res,constructors,on='constructorId')
#Creating driver full name column
dfRaceResults['full_name'] = dfRaceResults['forename'] + ' ' + dfRaceResults['surname']
#Removing unnecessary columns
dfRaceResults = dfRaceResults.drop(columns=['url_x','url_y','name_y','nationality_y','url','time_y','fp1_date','fp1_time','fp2_date','fp2_time','fp3_date','fp3_time','quali_date','sprint_date','sprint_time'])
#Display data
dfRaceResults.head(5)
Out[7]:
resultId raceId driverId constructorId number_x grid position positionText positionOrder points laps time_x milliseconds fastestLap rank fastestLapTime fastestLapSpeed statusId driverRef number_y code forename surname dob nationality_x year round circuitId name_x date quali_time constructorRef full_name
0 1 18 1 1 22 1 1 1 1 10.0 58 1:34:50.616 5690616 39 2 1:27.452 218.300 1 hamilton 44 HAM Lewis Hamilton 1985-01-07 British 2008 1 1 Australian Grand Prix 2008-03-16 \N mclaren Lewis Hamilton
1 5 18 5 1 23 3 5 5 5 4.0 58 +18.014 5708630 43 1 1:27.418 218.385 1 kovalainen \N KOV Heikki Kovalainen 1981-10-19 Finnish 2008 1 1 Australian Grand Prix 2008-03-16 \N mclaren Heikki Kovalainen
2 27 19 1 1 22 9 5 5 5 4.0 56 +46.548 5525103 53 3 1:35.462 209.033 1 hamilton 44 HAM Lewis Hamilton 1985-01-07 British 2008 2 2 Malaysian Grand Prix 2008-03-23 \N mclaren Lewis Hamilton
3 25 19 5 1 23 8 3 3 3 6.0 56 +38.450 5517005 19 7 1:35.922 208.031 1 kovalainen \N KOV Heikki Kovalainen 1981-10-19 Finnish 2008 2 2 Malaysian Grand Prix 2008-03-23 \N mclaren Heikki Kovalainen
4 57 20 1 1 22 3 13 13 13 0.0 56 \N \N 25 19 1:35.520 203.969 11 hamilton 44 HAM Lewis Hamilton 1985-01-07 British 2008 3 3 Bahrain Grand Prix 2008-04-06 \N mclaren Lewis Hamilton

2.General Statistics¶

2.1 About F1 circuits,drivers and constructors¶

In [8]:
#Circuits
df_circuits = circuits.groupby('country').agg({'name':'count'}).reset_index()
df_circuits.rename({'name':'circuits_count'},axis=1,inplace=True)
#Drivers
df_drivers = drivers.groupby('nationality').agg({'driverRef':'count'}).reset_index()
df_drivers = df_drivers.rename({'driverRef':'driver_count'},axis=1)
#Constructors
df_constructors = constructors.groupby('nationality').agg({'constructorRef':'count'}).reset_index()
df_constructors = df_constructors.rename({'constructorRef':'constructors_count'},axis=1)
In [9]:
df1=df_circuits.sort_values('circuits_count',ascending=True)
df2=df_drivers.sort_values('driver_count',ascending=True)
df3=df_constructors.sort_values('constructors_count',ascending=True)





from plotly.subplots import make_subplots


fig = make_subplots(
    rows=1, cols=3,
    subplot_titles=('Total circuits per country', 'Total drivers per country','Total constructors per country'),
    horizontal_spacing = 0.1,
    vertical_spacing = 0.5)

fig.add_trace(go.Bar(
                x=df1['circuits_count'],
                y=df1["country"],
                hovertext='Circuits',
                orientation='h'),
                row=1, col=1)

fig.add_trace(go.Bar(
                x=df2['driver_count'],
                y=df2["nationality"],
                hovertext='Drivers',
                orientation='h'),
                row=1, col=2)

fig.add_trace(go.Bar(
                x=df3['constructors_count'],
                y=df3["nationality"],
                hovertext='Constructors',
                orientation='h'),
                row=1, col=3)
fig.update_layout(title_text='<b> F1 countries information [Circuits,Drivers,Constructors]<b>', 
                  titlefont={'size':25},
                  title_x=0.33,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template='ggplot2',
                  paper_bgcolor='orange'
                 )

fig.show()

Circuits location on world map¶

In [10]:
from folium import plugins
coord=[]
for lat,lng in zip(circuits['lat'],circuits['lng']):
    coord.append([lat,lng])   
maps = folium.Map(location=[45.3656,9.1651],zoom_start=5,max_bounds=True,no_wrap=True,tiles='cartodbpositron') 
folium.plugins.Fullscreen().add_to(maps)
for i,j in zip(coord,circuits.name):
    marker = folium.Marker(
        location=i,
        icon=folium.Icon(icon="star",color='red'),
        popup="<strong>{0}</strong>".format(j))  
    marker.add_to(maps)
maps
Out[10]:
Make this Notebook Trusted to load map: File -> Trust Notebook

2.2 All time F1 records (drivers)¶

In [11]:
head = 10
#Driver wins
dfDriverWins = dfRaceResults[(dfRaceResults['position']== '1')]
dfDriverWins['position_rank'] = dfDriverWins['position'].astype(int)
dfDriverWins = dfDriverWins.groupby(['full_name','nationality_x'])['position_rank'].sum().reset_index()
dfDriverWins = dfDriverWins.sort_values(by=['position_rank'], ascending=False).head(head)
#Driver poles
dfDriverPoles = dfRaceResults[dfRaceResults['grid']== 1].groupby(by=['full_name','nationality_x'])['grid'].sum().reset_index()
dfDriverPoles = dfDriverPoles.sort_values(by=['grid'], ascending=False).head(head)
#Driver titles
dfDriverSum = dfRaceResults.groupby(['year','full_name'])['points'].sum().reset_index()
dfDriverTiles = dfDriverSum.loc[dfDriverSum.reset_index().groupby(['year'])['points'].idxmax()]
dfDriverTiles = dfDriverTiles['full_name'].value_counts().reset_index()
dfDriverTiles.rename(columns={'index':'driver','full_name':'titles'}, inplace = True)
dfDriverTiles = dfDriverTiles.sort_values(by=['titles'], ascending=False).head(head)
#Driver podiums
dfDriverPodiums = dfRaceResults[((dfRaceResults['positionText'].isin(['1','2','3']) ))]
dfDriverPodiums['position_rank'] = dfDriverPodiums['positionText']
dfDriverPodiums = dfDriverPodiums.groupby(['full_name','nationality_x'])['position_rank'].count().reset_index()
dfDriverPodiums = dfDriverPodiums.sort_values(by=['position_rank'], ascending=False).head(head)
#Driver points (not adjusted to current points system(since 2010))
dfDriverPoints = dfRaceResults.groupby(['full_name','nationality_x'])['points'].sum().reset_index()
dfDriverPoints = dfDriverPoints.sort_values(by=['points'], ascending=False).head(head)
#Drivers fastest laps
dfDriverFastestLap = dfRaceResults[(dfRaceResults['rank']== '1')]
dfDriverFastestLap['lap_rank'] = dfDriverFastestLap['rank'].astype(int)
dfDriverFastestLap = dfDriverFastestLap.groupby(['full_name','nationality_x'])['lap_rank'].sum().reset_index()
dfDriverFastestLap = dfDriverFastestLap.sort_values(by=['lap_rank'], ascending=False).head(head)

fig = make_subplots(
    rows=2, cols=3,
    subplot_titles=('Drivers with most wins','Drivers with most poles','Drivers with most titles','Drivers with most podiums','Drivers with most points scored in races','Drivers with most fastest laps'),
    horizontal_spacing = 0.12,
    vertical_spacing = 0.25)

fig.add_trace(go.Bar(
                x=dfDriverWins['full_name'],
                y=dfDriverWins["position_rank"],
                hovertext=dfDriverWins['nationality_x'],
                orientation='v'),
                row=1, col=1)
fig.add_trace(go.Bar(
                x=dfDriverPoles['full_name'],
                y=dfDriverPoles['grid'],
                hovertext = dfDriverPoles['nationality_x'],
                orientation='v'),
                row=1, col=2)
fig.add_trace(go.Bar(
                x=dfDriverTiles['driver'],
                y=dfDriverTiles['titles'], 
                orientation='v'),
                row=1, col=3)
fig.add_trace(go.Bar(
                x=dfDriverPodiums['full_name'],
                y=dfDriverPodiums["position_rank"],
                hovertext=dfDriverPodiums['nationality_x'],
                orientation='v'),
                row=2, col=1)
fig.add_trace(go.Bar(
                x=dfDriverPoints['full_name'],
                y=dfDriverPoints["points"],
                hovertext=dfDriverPoints['nationality_x'],
                orientation='v'),
                row=2, col=2)
fig.add_trace(go.Bar(
                x=dfDriverFastestLap['full_name'],
                y=dfDriverFastestLap['lap_rank'],
                hovertext=dfDriverFastestLap['nationality_x'],
                orientation='v'),
                row=2, col=3)   
                                                                                                        
fig.update_layout(title_text='<b> F1 drivers all time records<b>', 
                  titlefont={'size':25},
                  title_x=0.5,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template = "plotly_dark"
                 )

fig.show()

Notes:

  • Points system have changed in 2010 ,due to this fact, chart ' Drivers with most points' heavily favours these ones who were driving in 2010s/2020s.
  • Fastest laps are counted since 2004 due to lack of data before. Technically it is possible to get these data by merging 'lap_times.csv' with dataset ,however due to its size idea was dropped.

2.3 All time F1 records (constructors)¶

In [12]:
head = 10
#Constructor wins
dfConstructorWins = dfRaceResults[(dfRaceResults['position']== '1')]
dfConstructorWins['position_rank'] = dfConstructorWins['position'].astype(int)
dfConstructorWins = dfConstructorWins.groupby(['constructorRef'])['position_rank'].sum().reset_index()
dfConstructorWins = dfConstructorWins.sort_values(by=['position_rank'], ascending=False).head(head)
#Constructor poles
dfConstructorPoles = dfRaceResults[dfRaceResults['grid']== 1].groupby(by=['constructorRef'])['grid'].sum().reset_index()
dfConstructorPoles = dfConstructorPoles.sort_values(by=['grid'], ascending=False).head(head)
#Constructor titles(since 1958)
dfFilteredResults=dfRaceResults[(dfRaceResults['year'] > 1958) & (dfRaceResults['year'] < 2022)]
dfConstructorSum = dfFilteredResults.groupby(['year','constructorRef'])['points'].sum().reset_index()
dfConstructorTiles = dfConstructorSum.loc[dfConstructorSum.reset_index().groupby(['year'])['points'].idxmax()]
dfConstructorTiles = dfConstructorTiles['constructorRef'].value_counts().reset_index()
dfConstructorTiles.rename(columns={'index':'constructor','constructorRef':'titles'}, inplace = True)
dfConstructorTiles = dfConstructorTiles.sort_values(by=['titles'], ascending=False).head(head)
#Constructor podiums finishes
dfConstructorPodiums = dfRaceResults[((dfRaceResults['positionText'].isin(['1','2','3']) ))]
dfConstructorPodiums['position_rank'] = dfConstructorPodiums['positionText']
dfConstructorPodiums = dfConstructorPodiums.groupby(['constructorRef'])['position_rank'].count().reset_index()
dfConstructorPodiums = dfConstructorPodiums.sort_values(by=['position_rank'], ascending=False).head(head)
#Constructor points (not adjusted to current points system(since 2010))
dfConstructorPoints = dfRaceResults.groupby(['constructorRef'])['points'].sum().reset_index()
dfConstructorPoints = dfConstructorPoints.sort_values(by=['points'], ascending=False).head(head)
#Constructor fastest laps
dfConstructorFastestLap = dfRaceResults[(dfRaceResults['rank']== '1')]
dfConstructorFastestLap['lap_rank'] = dfConstructorFastestLap['rank'].astype(int)
dfConstructorFastestLap = dfConstructorFastestLap.groupby(['constructorRef'])['lap_rank'].sum().reset_index()
dfConstructorFastestLap = dfConstructorFastestLap.sort_values(by=['lap_rank'], ascending=False).head(head)

fig = make_subplots(
    rows=2, cols=3,
    subplot_titles=('Constructors with most wins','Constructors with most poles','Constructor with most constructor titles','Constructors with most podium finishes','Constructors with most points scored in races','Constructors with most fastest laps'),
    horizontal_spacing = 0.12,
    vertical_spacing = 0.25)

fig.add_trace(go.Bar(
                x=dfConstructorWins['constructorRef'],
                y=dfConstructorWins["position_rank"],
                orientation='v'),
                row=1, col=1)
fig.add_trace(go.Bar(
                x=dfConstructorPoles['constructorRef'],
                y=dfConstructorPoles['grid'],
                orientation='v'),
                row=1, col=2)
fig.add_trace(go.Bar(
                x=dfConstructorTiles['constructor'],
                y=dfConstructorTiles['titles'], 
                orientation='v'),
                row=1, col=3)
fig.add_trace(go.Bar(
                x=dfConstructorPodiums['constructorRef'],
                y=dfConstructorPodiums["position_rank"],
                orientation='v'),
                row=2, col=1)
fig.add_trace(go.Bar(
                x=dfConstructorPoints['constructorRef'],
                y=dfConstructorPoints["points"],
                orientation='v'),
                row=2, col=2)
fig.add_trace(go.Bar(
                x=dfConstructorFastestLap['constructorRef'],
                y=dfConstructorFastestLap['lap_rank'],
                orientation='v'),
                row=2, col=3)   
                                                                                                        
fig.update_layout(title_text='<b> F1 constructor all time records<b>', 
                  titlefont={'size':25},
                  title_x=0.5,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template = "plotly_dark",
                 )

fig.show()

Notes:

  • Constructor championship title is being rewarded since 1958.
  • Data grouped by constructor reference to avoid potential problems with merging same teams into one( due to different names at certain time periods).

2.4 F1 drivers season records¶

In [13]:
#Driver wins most per season
dfRaceResults[dfRaceResults.position== 1].groupby(['year', 'full_name']).resultId.count().groupby('year')
dfDriverWins = dfRaceResults[(dfRaceResults['position']== '1')]
dfDriverWins['wins_per_season'] = dfDriverWins['position'].astype(int)
dfDriverWins = dfDriverWins.groupby(['full_name','year'])['wins_per_season'].count().reset_index()
dfDriverWins = dfDriverWins.sort_values(['year','wins_per_season'],ascending=[True,False]).drop_duplicates(['year'])

#Driver poles most per season
dfDriverPoles = dfRaceResults[dfRaceResults['grid']== 1].groupby(by=['full_name','year'])['grid'].sum().reset_index()
dfDriverPoles = dfDriverPoles.sort_values(['year','grid'],ascending=[True,False]).drop_duplicates(['year'])

#Driver most podiums per season
dfDriverPodiums = dfRaceResults[((dfRaceResults['positionText'].isin(['1','2','3']) ))]
dfDriverPodiums['podiums_per_season'] = dfDriverPodiums['positionText']
dfDriverPodiums = dfDriverPodiums.groupby(['full_name','year'])['podiums_per_season'].count().reset_index()
dfDriverPodiums = dfDriverPodiums.sort_values(['year','podiums_per_season'],ascending=[True,False]).drop_duplicates(['year'])

fig = make_subplots(
    rows=3, cols=1,
    subplot_titles=('Most wins per season','Most poles per season','Most podiums per season'),
    horizontal_spacing = 0.5,
    vertical_spacing = 0.12)


fig.add_trace(go.Bar(
                x=dfDriverWins['year'],
                y=dfDriverWins["wins_per_season"],
                hovertext=dfDriverWins['full_name'],
                orientation='v'),
                row=1, col=1)
fig.add_trace(go.Bar(
                x=dfDriverPoles['year'],
                y=dfDriverPoles["grid"],
                hovertext=dfDriverPoles['full_name'],
                orientation='v'),
                row=2, col=1)
fig.add_trace(go.Bar(
                x=dfDriverPodiums['year'],
                y=dfDriverPodiums["podiums_per_season"],
                hovertext=dfDriverPodiums['full_name'],
                orientation='v'),
                row=3, col=1)
                                                                                                        
fig.update_layout(title_text='<b> F1 most of the every season record<b>', 
                  titlefont={'size':25},
                  title_x=0.5,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template = "plotly_white"
                 )

fig.show()

3. Season leaderboards¶

3.1 Driver leaderboards¶

In [14]:
traces = []
buttons = []
updatemenus = [{'active':0, "buttons":buttons}]

driver_leaderboard = dfRaceResults.groupby(['year','full_name']).agg({'points':'sum'}).reset_index()
driver_leaderboard = driver_leaderboard.sort_values(['year','points'],ascending=[True,False])

years = sorted(driver_leaderboard.year.unique())
for i, year in enumerate(years):
    visible = [False] * len(years)
    visible[i] = True
    df = driver_leaderboard[driver_leaderboard.year == year]
    colors = ['red',] * len(df.full_name.unique())
    colors[0] = 'gold'
    colors[1] = 'silver'
    colors[2] = '#cd7f32'
    traces.append(
        px.bar(data_frame=df,x='full_name', y='points').update_traces(visible=True if i==0 else False,marker_color=colors).data[0]
    )
    buttons.append(dict(label=str(year),
                        method="update",
                        args=[{"visible":visible},
                              {"title":f"Driver leaderboard season {year}"}]))


fig = go.Figure(data=traces,
                 layout=dict(updatemenus=updatemenus))
fig.update_layout(title='Driver leaderboard season 1950',title_x=0.5,template='ggplot2')
fig.show()

Notes:

  • Sprint races point not included for 2021 and 2022

3.2 Constructor leaderboards since 1958¶

In [15]:
traces = []
buttons = []
updatemenus = [{'active':0, "buttons":buttons}]

dfFilteredResults=dfRaceResults[(dfRaceResults['year'] > 1958)]
constructor_leaderboard = dfFilteredResults.groupby(['year','constructorRef']).agg({'points':'sum'}).reset_index()
constructor_leaderboard = constructor_leaderboard.sort_values(['year','points'],ascending=[True,False])

years = sorted(constructor_leaderboard.year.unique())
for i, year in enumerate(years):
    visible = [False] * len(years)
    visible[i] = True
    df = constructor_leaderboard[constructor_leaderboard.year == year]
    colors = ['red'] * len(df.constructorRef.unique())
    colors[0] = 'gold'
    colors[1] = 'silver'
    colors[2] = '#cd7f32'
    
    traces.append(
        px.bar(data_frame=df,x='constructorRef', y='points').update_traces(visible=True if i==0 else False,marker_color=colors).data[0]
    )
    buttons.append(dict(label=str(year),
                        method="update",
                        args=[{"visible":visible},
                              {"title":f"Constructor leaderboard season {year}"}]))


fig = go.Figure(data=traces,
                 layout=dict(updatemenus=updatemenus))
fig.update_layout(title='Constructor leaderboard season 1958',title_x=0.5,template='ggplot2')
fig.show()

4. Championships battles¶

4.1 Driver championship battles (1950-2022)¶

In [16]:
def championship_battle(year):
    season = dfRaceResults[dfRaceResults.year==year]
    championship_points = pd.DataFrame(season.groupby(['raceId', 'full_name'])['points'].sum().groupby('full_name').cumsum())
    championship_points = championship_points.reset_index()
    return championship_points.sort_values(['raceId','points'],ascending=[True,True])
In [23]:
dropdown = widgets.Dropdown(options=sorted(dfRaceResults.year.unique()),value=2021,description='Season')
display(dropdown)
Dropdown(description='Season', index=71, options=(1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, …
In [25]:
df = championship_battle(dropdown.value)
fig = px.bar(df, y="full_name", x="points",animation_frame="raceId", range_x=[0,500])
fig.update_layout(title_text = ('<b> F1 driver championship battle of <b>'+ str(dropdown.value)), 
                  titlefont={'size':15},
                  title_x=0.5,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template='plotly_dark',
                 )
fig.show()

4.2 Constructors championship battles (1950-2022)¶

In [19]:
def con_championship_battle(year):
    dfFilteredResults=dfRaceResults[(dfRaceResults['year'] > 1958)]
    season = dfFilteredResults[dfFilteredResults.year==year]
    championship_points = pd.DataFrame(season.groupby(['raceId', 'constructorRef'])['points'].sum().groupby('constructorRef').cumsum())
    championship_points = championship_points.reset_index()
    return championship_points.sort_values(['raceId','points'],ascending=[True,True])
In [20]:
dropdown = widgets.Dropdown(options=sorted(dfFilteredResults.year.unique()),value=2021,description='Season')
display(dropdown)
Dropdown(description='Season', index=62, options=(1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, …
In [21]:
df = con_championship_battle(dropdown.value)
fig = px.bar(df, y="constructorRef", x="points",animation_frame="raceId", range_x=[0,800])
fig.update_layout(title_text = ('<b> F1 constructor championship battle of <b>'+ str(dropdown.value)), 
                  titlefont={'size':15},
                  title_x=0.5,
                  showlegend=False,
                  autosize=True,
                  height=1000,
                  template='plotly_dark',
                 )
fig.show()